package com.easypay.reactive8583.server;

import com.easypay.reactive8583.AbstractIso8583Connector;
import com.easypay.reactive8583.iso.MessageFactory;
import com.easypay.reactive8583.netty.pipeline.Iso8583ChannelInitializer;
import com.solab.iso8583.IsoMessage;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import java.net.InetSocketAddress;

/**
 * @author pandans
 * @date 2021/03/04 18:43
 * @Description:
 */
public class Iso8583Server<T extends IsoMessage> extends AbstractIso8583Connector<ServerConfiguration, ServerBootstrap, T> {
    private int port;
    private ServerConfiguration config;
    private MessageFactory<T> messageFactory;
    private InetSocketAddress socketAddress;

    public Iso8583Server(int port, ServerConfiguration configuration, MessageFactory<T> isoMessageFactory) {
        super(configuration, isoMessageFactory);
        this.port = port;
        this.config = configuration;
        this.messageFactory = isoMessageFactory;
        this.socketAddress = new InetSocketAddress(port);
    }

    @Override
    protected ServerBootstrap createBootstrap() {
        ServerBootstrap bootstrap = new ServerBootstrap();
        Boolean tcpNoDelay = java.lang.Boolean.parseBoolean(System.getProperty("nfs.rpc.tcp.nodelay", "true"));
        bootstrap.group(bossEventLoopGroup, workerEventLoopGroup)
                .channel(NioServerSocketChannel.class)
            .childOption(ChannelOption.TCP_NODELAY, tcpNoDelay)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .localAddress(socketAddress)
                .childHandler(
                        new Iso8583ChannelInitializer<Channel, ServerBootstrap, ServerConfiguration>(
                                configuration,
                        configurer,
                        workerEventLoopGroup,
                                (MessageFactory<IsoMessage>)isoMessageFactory,
                        getMessageHandler()
                )
            );
        configureBootstrap(bootstrap);
        bootstrap.validate();
        return bootstrap;
    }

    public void start() throws InterruptedException {
        bootstrap.bind().addListener(
                (ChannelFuture future) -> {
                    setChannel(future.channel());
                    logger.info("Server is started and listening at {}", getChannel().localAddress());
                }
        ).sync().await();
    }
    @Override
    public void shutdown() {
        stop();
        super.shutdown();
    }

    /**
     * @return True if server is ready to accept connections.
     */
    public boolean isStarted(){
        Channel channel = getChannel();
        return channel != null && channel.isOpen();
    }

    public void stop() {
        Channel channel = getChannel();
        if (channel == null) {
            logger.info("The Server is not started...");
            return;
        }
        logger.info("Stopping the Server...");
        if(channel != null) {
            try {
                channel.deregister();
                channel.close().syncUninterruptibly();
                logger.info("Server was Stopped.");
            } catch (Exception e) {
                logger.error("Error while stopping the server", e);
            }
        }
    }
}
